package com.free.note.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.free.common.core.constant.FileFtpConstants;
import com.free.common.core.constant.RedisNodeCacheConstants;
import com.free.common.core.enums.Classify;
import com.free.common.core.pojo.FileInfo;
import com.free.common.core.utils.DateUtils;
import com.free.common.core.utils.NumberUtil;
import com.free.compilation.domain.ZoneCompilation;
import com.free.compilation.domain.ZoneCompilationStep;
import com.free.compilation.mapper.ZoneCompilationMapper;
import com.free.compilation.service.ZoneCompilationStepService;
import com.free.note.domain.NoteRandomCount;
import com.free.note.domain.TypeTree;
import com.free.note.domain.ZoneNote;
import com.free.note.feign.FileClientService;
import com.free.note.mapper.ZoneNoteMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 笔记Service业务层处理
 *
 * @author YHL
 * @date 2023-02-26
 */
@Service
@Slf4j
public class ZoneNoteService extends ServiceImpl<ZoneNoteMapper, ZoneNote> {


    private final List classList = new ArrayList<>(Arrays.asList(105, 107, 110, 111));

    @Value("${file.domain}")
    String domain;
    @Value("${base.url}")
    String baseUrl;

    @Autowired
    TypeTreeService typeTreeService;

    @Autowired
    NoteRandomCountService noteRandomCountService;
    @Autowired
    ZoneCompilationStepService zoneCompilationStepService;
    @Autowired
    ZoneCompilationMapper compilationMapper;
    @Autowired
    FileClientService fileClientService;
    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 查询笔记
     *
     * @param id 笔记主键
     * @return 笔记
     */
    public ZoneNote selectZoneNoteById(Long id) {
        return this.baseMapper.selectById(id);
    }

    /**
     * 查询笔记列表
     *
     * @param zoneNote 笔记
     * @return 笔记
     */
    public List<ZoneNote> selectZoneNoteList(ZoneNote zoneNote) {
        QueryWrapper<ZoneNote> wrapper = new QueryWrapper<>();
        wrapper.like(ObjectUtils.isNotEmpty(zoneNote.getTitle()), "title", zoneNote.getTitle());
        wrapper.like(ObjectUtils.isNotEmpty(zoneNote.getClassify()), "classify", zoneNote.getClassify());
        wrapper.like(ObjectUtils.isNotEmpty(zoneNote.getExternalDisplay()), "external_display", zoneNote.getExternalDisplay());
        wrapper.like(ObjectUtils.isNotEmpty(zoneNote.getAiAudioToText()), "ai_audio_to_text", zoneNote.getAiAudioToText());
        wrapper.like(ObjectUtils.isNotEmpty(zoneNote.getExternalDisplay()), "external_display", zoneNote.getExternalDisplay());
        wrapper.orderByDesc("id");
        List<ZoneNote> zoneNotes = this.baseMapper.selectList(wrapper);
        zoneNotes.forEach(note -> {
            List<ZoneCompilation> compilations = this.baseMapper.selectRelevanceCompilation(note.getId());
            note.setCompilations(compilations);
        });
//        SELECT * from zone_compilation WHERE id in ( select compilation_id from zone_compilation_step where note_id = 700 )
        return zoneNotes;
    }

    /**
     * 新增笔记
     *
     * @param zoneNote 笔记
     * @return 结果
     */
    public ZoneNote insertZoneNote(ZoneNote zoneNote) {
        zoneNote.setCreateTime(DateUtils.getNowDate());
        setNewLabel(zoneNote);
        if (ObjectUtil.equal(zoneNote.getClassify(), 124)) {
            zoneNote.setFilePath(zoneNote.getContent());
            zoneNote.setAiAudioToText(1);
        }else {
            zoneNote.setAiAudioToText(0);
        }
        int insert = this.baseMapper.insert(zoneNote);
//        fileSynchronization(zoneNote);
//        redisTemplate.delete(RedisNodeCacheConstants.getShowAgainRandomList());
        return zoneNote;
    }

    private void fileSynchronization(ZoneNote zoneNote) {
        ThreadUtil.execute(() -> {
            log.info("开始同步-----");
//            String downFilePath = System.getProperty("java.io.tmpdir");
            String downFilePath = "/data/free/manage/fileTemp";
            String ftpDownPrefix = FileFtpConstants.FTP_DOWN_PREFIX;
            List<File> files = new ArrayList();
            String[] split = new String[]{};
            List<String> contentList = null;
            if (classList.contains(zoneNote.getClassify())) {
                String urls = Base64.decodeStr(zoneNote.getContent());
                log.info("下载的文件地址【{}】", urls);
                log.info("下载的文件domain【{}】", domain);
                split = urls.split(",");
                if (ObjectUtil.isNotEmpty(split)) {
                    contentList = new ArrayList<>(split.length);
                    log.info("下载的文件大小【{}】", 1);
                    for (String s : split) {
                        if (!s.startsWith(ftpDownPrefix)) {
                            log.info("排除--》{}", s);
                            return;
                        }
                        log.info("下载的文件大小【{}】", s);
                        String[] fileSlot = s.split("/");
                        log.info("下载的文件大小fileSlot【{}】", fileSlot);
                        try {
                            File file = HttpUtil.downloadFileFromUrl(domain + s, new File(downFilePath + File.separator + fileSlot[fileSlot.length - 1]), new StreamProgress() {

                                @Override
                                public void start() {
                                    Console.log("开始下载。。。。");
                                }

                                /**
                                 * @param l
                                 * @param l1
                                 */
                                @Override
                                public void progress(long l, long l1) {
                                    Console.log("已下载：{}--{}", FileUtil.readableFileSize(l), FileUtil.readableFileSize(l1));
                                }

                                @Override
                                public void finish() {
                                    Console.log("下载完成！");
                                }
                            });
                            contentList.add(file.getName());
                            log.info("下载的文件大小【{}】", file.length());
                            if (file.exists()) {
                                files.add(file);
                            }
                        } catch (Exception e) {
                            log.info("下载的文件出错【{}】", e.getMessage());
                            e.printStackTrace();
                        }
                    }
                }
            } else {
                files = new ArrayList<>(1);
            }
            File noteTxt = new File(downFilePath + "/" + zoneNote.getId() + "、" + zoneNote.getTitle() + ".txt");
            FileUtil.writeUtf8String(JSONUtil.toJsonStr(zoneNote), noteTxt);
            files.add(noteTxt);
            log.info("已准备的文件【{}】", files);
            //匿名登录（无需帐号密码的FTP服务器）
//            Ftp ftp = new Ftp("192.168.2.120", 21, "note", "yhl580230");
            Ftp ftp = new Ftp("192.168.2.120");
//            Ftp ftp = new Ftp("192.168.31.120", 21);1
            //进入远程目录
            //上传本地文件
            LocalDateTime now = LocalDateTimeUtil.of(zoneNote.getCreateTime());
            String filePath = "/" + "Note" + "/" + zoneNote.getClassifyStr() + "/" + now.getYear() + "/" + NumberUtil.prefix10(now.getMonthValue()) + "/" + NumberUtil.prefix10(now.getDayOfMonth()) + "/" + zoneNote.getId();
            ftp.mkDirs(filePath);
            for (File file : files) {
                try {
                    ftp.upload(filePath, file);
                    log.info("文件同步完成【{}】【{}】", filePath,file.getName());
                } catch (Exception e) {
                    log.info("写入文件错误【{}】】【{}】", filePath,file.getName());
                    e.printStackTrace();
                }
                file.delete();
            }
            try {
                ftp.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //处理之后的内容
            if (ObjectUtil.isNotEmpty(contentList)) {
                String ftpPathFileName = Base64.decodeStr(zoneNote.getContent());
                String content = "/statics/" + filePath + ftpPathFileName.substring(ftpPathFileName.lastIndexOf("/"));
                log.info("处理过后的路径：{}", content);
                zoneNote.setContent(Base64.encode(content));
                this.baseMapper.updateById(zoneNote);
            }
            for (String file : split) {
                try {
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.setFileName(file);
                    fileClientService.delete(fileInfo);
//
                    log.info("文件删除完成【{}】", file);
                } catch (Exception e) {
                    log.info("文件删除错误【{}】", e.getMessage());
                    e.printStackTrace();
                }
            }
            log.info("文件同步完成【{}】", zoneNote.getTitle());

        });
    }

    private void setNewLabel(ZoneNote zoneNote) {
        if (ObjectUtils.isNotEmpty(zoneNote.getNewLabel())) {
            TypeTree typeTree = new TypeTree();
            if (ObjectUtils.isNotEmpty(zoneNote.getTreeId())) {
                String[] split = zoneNote.getTreeId().split(",");
                Long pid = Long.parseLong(split[split.length - 1]);
                typeTree.setPid(pid);
            }
            typeTree.setLabel(zoneNote.getNewLabel());
            Long count = typeTreeService.getBaseMapper().selectCount(new QueryWrapper<>(typeTree));
            if (count == 0) {
                typeTreeService.save(typeTree);
                zoneNote.setTreeId(zoneNote.getTreeId() + "," + typeTree.getId());
            }
        }
    }

    /**
     * 修改笔记
     *
     * @param zoneNote 笔记
     * @return 结果
     */
    public ZoneNote updateZoneNote(ZoneNote zoneNote) {
        zoneNote.setUpdateTime(DateUtils.getNowDate());
        setNewLabel(zoneNote);
        int updateZoneNote = this.baseMapper.updateById(zoneNote);
//        fileSynchronization(zoneNote);
//        redisTemplate.delete(RedisNodeCacheConstants.getShowAgainRandomList());
        return zoneNote;
    }

    /**
     * 批量删除笔记
     *
     * @param ids 需要删除的笔记主键
     * @return 结果
     */
    public int deleteZoneNoteByIds(Long[] ids) {
//        redisTemplate.delete(RedisNodeCacheConstants.getShowAgainRandomList());

        List<Long> longs = Arrays.asList(ids);
        longs.forEach(e -> {
            QueryWrapper<ZoneCompilationStep> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("note_id", e);
            Long aLong = zoneCompilationStepService.getBaseMapper().selectCount(queryWrapper);
            Assert.isTrue(aLong == 0, "有关联的合集，无法删除");
        });

        return this.baseMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除笔记信息
     *
     * @param id 笔记主键
     * @return 结果
     */
    public int deleteZoneNoteById(Long id) {
//        redisTemplate.delete(RedisNodeCacheConstants.getShowAgainRandomList());
        return this.baseMapper.deleteById(id);
    }

    /**
     * 随机获取一个
     *
     * @return
     */
    public ZoneNote randomOne(String compilationId) {
        ZoneNote zoneNote = (ZoneNote) redisTemplate.opsForList().leftPop(RedisNodeCacheConstants.getShowAgainRandomList(compilationId));
        if (ObjectUtil.isEmpty(zoneNote)) {
            Set members = redisTemplate.opsForSet().members(RedisNodeCacheConstants.getNotShowAgainSet());
            QueryWrapper<ZoneNote> queryWrapper = new QueryWrapper();
            queryWrapper.notIn(ObjectUtil.isNotEmpty(members), "id", members).in("classify", "101", "109 ").orderByAsc(" rand()")
                    .apply(ObjectUtil.isNotEmpty(compilationId), "id in ( select note_id from zone_compilation_step where compilation_id =   " + compilationId + ")");
            List<ZoneNote> zoneNotes = this.baseMapper.selectList(queryWrapper);
            redisTemplate.opsForList().rightPushAll(RedisNodeCacheConstants.getShowAgainRandomList(compilationId), zoneNotes);
            zoneNote = (ZoneNote) redisTemplate.opsForList().leftPop(RedisNodeCacheConstants.getShowAgainRandomList(compilationId));
            Assert.notNull(zoneNote, "没有合适的题型");
        }
        Long id = zoneNote.getId();
        NoteRandomCount byId = noteRandomCountService.getById(id);
        if (byId == null) {
            byId = new NoteRandomCount();
            byId.setNoteId(id);
//            byId.setId(id);
            byId.setRandomCount(0L);
        }
        byId.setRandomCount(byId.getRandomCount() + 1);
        noteRandomCountService.saveOrUpdate(byId);
        return zoneNote;
    }

    /**
     * @param id
     */
    public void notShowAgain(Long id) {
        redisTemplate.opsForSet().add(RedisNodeCacheConstants.getNotShowAgainSet(), id);
    }

    /**
     *
     */
    public Long getRemainingQuantity(String compilationId) {
        return redisTemplate.opsForList().size(RedisNodeCacheConstants.getShowAgainRandomList(compilationId));
    }

    public void refreshQuantity(String compilationId) {
        redisTemplate.delete(RedisNodeCacheConstants.getShowAgainRandomList(compilationId));
    }

    /**
     * @return
     */
    public ZoneNote getNeedRecognitionOne() {
        QueryWrapper<ZoneNote> wrapper = new QueryWrapper<>();
        wrapper.eq("classify", Classify.PIC.getClassify());
        wrapper.eq("ai_recognition", 0);
        Page<ZoneNote> zoneNotePage = this.baseMapper.selectPage(new Page<>(1, 1), wrapper);
        return ObjectUtil.isNotEmpty(zoneNotePage.getRecords()) ? zoneNotePage.getRecords().get(0) : null;
    }

    /**
     * @param classify
     * @return
     */
    public ZoneNote getRandomOneByClassify(String classify) {
        Object obj = redisTemplate.opsForList().leftPop(RedisNodeCacheConstants.getRandomClassifyIds(classify));
        if (ObjectUtil.isEmpty(obj)) {
            List<Integer> ids = this.baseMapper.randomOne(classify);
            if (ObjectUtil.isEmpty(ids)) {
                return null;
            }
            redisTemplate.opsForList().leftPushAll(RedisNodeCacheConstants.getRandomClassifyIds(classify), ids);
            obj = redisTemplate.opsForList().leftPop(RedisNodeCacheConstants.getRandomClassifyIds(classify));
        }
        ZoneNote zoneNote = this.baseMapper.selectById(obj.toString());
        if (ObjectUtil.isEmpty(zoneNote)) {
            return null;
        }
        //查询关联的合集
        ZoneCompilationStep step = new ZoneCompilationStep();
        step.setNoteId(zoneNote.getId());
        List<ZoneCompilationStep> steps = this.zoneCompilationStepService.selectZoneCompilationStepList(step);
        if (ObjectUtil.isNotEmpty(steps)) {
            List<ZoneCompilation> zoneCompilations = this.compilationMapper.selectBatchIds(steps.stream().map(ZoneCompilationStep::getCompilationId).collect(Collectors.toList()));
            zoneNote.setCompilations(zoneCompilations);
        }
        return zoneNote;
    }
//    @Override
//    public IPage<User> getLimit() {
//        // 参数一:当前页面 currentPage  参数二：每页展示数 pageSize
//        Page<User> page = new Page<>(1, 3);
//        QueryWrapper<User> wrapper = new QueryWrapper<>();
//        wrapper.like("realname", "高");
//        IPage<User> userIPage = userMapper.selectPage(page, wrapper);
//        return userIPage;
//    }

    public List<String> getSwiperList() {
        QueryWrapper<ZoneNote> wrapper = new QueryWrapper<>();
        wrapper.eq("classify", 110);
        wrapper.eq("external_display", 1);
        wrapper.orderByDesc("classify", "RAND()");
        Page<ZoneNote> page = this.baseMapper.selectPage(new Page<>(1, 10), wrapper);
        List<String> retList = new ArrayList<>();
        page.getRecords().forEach(note -> {
            String uri = Base64.decodeStr(note.getContent());
            retList.add(baseUrl + uri);
        });
        return retList;
    }

    public ZoneNote getNeedFileSynchronization() {
        QueryWrapper<ZoneNote> wrapper = new QueryWrapper<>();
        wrapper.eq("file_synchronization", 0);
        Page<ZoneNote> zoneNotePage = this.baseMapper.selectPage(new Page<>(1, 1), wrapper);
        return ObjectUtil.isNotEmpty(zoneNotePage.getRecords()) ? zoneNotePage.getRecords().get(0) : null;
    }
}
